<?php

namespace plugin\cms\app\bll;

use app\bll\Datadict;
use core\lib\schema\Table as SchemaTable;
use Exception;
use Illuminate\Database\Schema\Blueprint;
use support\Db;

/**
 * 模型字段
 */
class ModelField
{
    /**
     * 获取一个字段信息
     *
     * @param integer $id
     * @return array
     */
    public function find(int $id): array
    {
        $table = Db::table('cms_model_field')
            ->where('id', '=', $id)
            ->first();
        return $table === null ? [] : get_object_vars($table);
    }

    /**
     * 查找字段
     *
     * @param integer $modelid
     * @return array
     */
    public function select(int $modelid): array
    {
        return  Db::table('cms_model_field')
            ->where('modelid', '=', $modelid)
            ->orderByRaw(' `group` ASC,sort ASC')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }

    /**
     * 某字段最大排序值
     *
     * @param string $tablename
     * @return integer
     */
    public function maxSort(int $modelid): int
    {
        $maxsort = Db::table('cms_model_field')
            ->where('modelid', '=', $modelid)
            ->max('sort');
        return $maxsort ? $maxsort : 0;
    }


    /**
     * 添加字段
     *
     * @param array $fieldData
     * @param string $tablename
     * @throws Exception
     * @return void
     */
    public function batchAdd(array $fieldData, array $modelinfo)
    {

        try {
            $tablename   = $modelinfo['tablename'];
            $schemaTable = new SchemaTable($tablename);
            $columns     = $schemaTable->getColumns();
            $columns     = array_column($columns, 'name');

            foreach ($fieldData as $key => $field) {
                //验证表中字段是否存在
                if (in_array($field['name'], $columns)) {
                    throw new Exception('字段已存在【' . $field['name'] . '】');
                }
            }

            $maxsort = $this->maxSort($modelinfo['id']);
            foreach ($fieldData as $key => $field) {
                $maxsort++;
                $field['sort']      = $maxsort;
                $field['modelid']   = $modelinfo['id'];
                Db::table('cms_model_field')
                    ->insert($field);
                //循环添加字段
                Db::schema()->table($tablename, function (Blueprint $table) use ($field) {
                    $this->addColumn($table, $field['type'], $field['name']);
                });
            }
        } catch (Exception $ex) {

            throw  $ex;
        }
    }


    /**
     * 批量编辑
     *
     * @param array $newfields
     * @param integer $modelid
     * @return void
     */
    public function batchEdit(array $newfields, array $modelinfo)
    {
        //先校验字段名重复
        $bllModel    = new Model();

        $tablename   =  $modelinfo['tablename'];
        $schemaTable = new SchemaTable($tablename);
        $columns     = $schemaTable->getColumns(); //真实表里的字段
        $columns     = array_column($columns, 'name');


        $modelfields     = $this->select($modelinfo['id'], $modelinfo['siteid']);  //model表里的字段
        $modelexitfields = [];

        foreach ($modelfields as $field) {
            $modelexitfields[$field['name']] = $field['id'];
        }

        $diffcloumns = array_diff($columns, array_keys($modelexitfields));
        $diffcloumns = array_values($diffcloumns);
        foreach ($newfields as $key => $field) {

            if (in_array($field['name'],  $diffcloumns)) {
                throw new Exception($field['name'] . '字段已存在');
            }

            if (
                isset($modelexitfields[$field['name']])
                && $modelexitfields[$field['name']] != $key
            ) {
                throw new Exception($field['name'] . '字段已存在');
            }
        }

        try {
            foreach ($newfields as $key => $newfield) {
                $oldfield = $this->find($key);
                if ($newfield['name'] != $oldfield['name']) {
                    Db::schema()->table($modelinfo['tablename'], function (Blueprint $table) use ($oldfield, $newfield) {
                        //修改字段名
                        $table->renameColumn($oldfield['name'], $newfield['name']);
                    });
                }

                if ($newfield['type'] != $oldfield['type']) {
                    Db::schema()->table($modelinfo['tablename'], function (Blueprint $table) use ($oldfield, $newfield) {

                        //修改字段类型
                        $this->editColumn($table, $newfield['type'], $newfield['name']);
                    });
                }

                $newfield['editor'] = isset($newfield['editor']) ? $newfield['editor'] : 0;
                $newfield['contribute'] = isset($newfield['contribute']) ? $newfield['contribute'] : 0;
                $newfield['require'] = isset($newfield['require']) ? $newfield['require'] : 0;
                Db::table('cms_model_field')
                    ->where('id', '=', $key)
                    ->update($newfield);
            }
        } catch (Exception $ex) {

            throw $ex;
        }
    }


    /**
     * 批量删除
     *
     * @param array $ids
     * @throws Exception
     * @return void
     */
    public function batchDelete(array $ids, array $modelinfo)
    {

        foreach ($ids as $id) {

            try {
                $field = $this->find($id);
                Db::schema()->table($modelinfo['tablename'], function (Blueprint $table) use ($field) {

                    $table->dropColumn($field['name']);
                });

                Db::table('cms_model_field')
                    ->where('id', '=', $id)
                    ->delete();
            } catch (Exception $ex) {
                throw  $ex;
            }
        }
    }

    /**
     * 保存字段 自动判断删除 修改还是新增
     *
     * @param array $data
     * @return void
     */
    public function batchsave(array $data, array $modelinfo)
    {
        try {
            $tempfieldname = [];
            //先检查字段名有无重复
            foreach ($data as $d) {
                if (in_array(trim($d['name']), $tempfieldname)) {
                    throw new Exception($d['name'] . '字段重复');
                }
                $tempfieldname[] = trim($d['name']);
            }
            $addarr = [];
            $delarr = [];
            $editarr = [];
            foreach ($data as $key => $d) {
                $d['sort'] = $key;
                switch ($d['op']) {
                    case 'add':
                        unset($d['op']);
                        unset($d['id']);
                        $addarr[] = $d;
                        break;
                    case 'delete':
                        $delarr[] = $d['id'];
                        break;
                    case 'edit':
                        $id = $d['id'];
                        unset($d['op']);
                        unset($d['id']);
                        $editarr[$id] = $d;

                        break;
                }
            }
            if ($delarr) {
                $this->batchDelete($delarr, $modelinfo);
            }

            if ($addarr) {
                $this->batchAdd($addarr, $modelinfo);
            }

            if ($editarr) {

                $this->batchEdit($editarr, $modelinfo);
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }



    /**
     * 获取字段
     *
     * @return array
     */
    public function selField(int $modelid): array
    {
        $fields = Db::table('cms_model_field')
            ->where('modelid', '=', $modelid)
            ->orderBy('sort')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
        $formatfields = [];
        foreach ($fields as $field) {
            $formatfields[$field['group']][] = $field;
        }
        return $formatfields;
    }

    /**
     * 获取投稿字段
     *
     * @param integer $modelid
     * @return array
     */
    public function selContributeField(int $modelid): array
    {
        return Db::table('cms_model_field')
            ->where('modelid', '=', $modelid)
            ->where('contribute', '=', 1)
            ->orderBy('sort')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }



    /**
     * 获取查询字段
     *
     * @param integer $modelid
     * @return array
     */
    public function selSearchField(int $modelid): array
    {
        $fields = Db::table('cms_model_field')
            ->where('modelid', '=', $modelid)
            ->where('search', '<>', 0)
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
        

        $bllDatadict = new Datadict();
        foreach ($fields as $key => $field) {
            if ($field['sourcetype'] == 1) {
                //获取数据字典
                $fields[$key]['datadict'] = $bllDatadict->find($field['sourceval']);
            }
        }

        return $fields;
    }



    /**
     * 添加字段 私有
     *
     * @param Blueprint $table
     * @param string $type
     * @param string $name
     * @param mixed $default
     * @return void
     */
    private function addColumn(
        Blueprint $table,
        string $type,
        string $name,
        mixed $default = ''
    ) {
        switch ($type) {
            case 'string':
                $table->string($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'decimal':
                $table->decimal($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'text':
                $table->text($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'integer':
                $table->integer($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'datetime':
                $table->dateTime($name)->default($default == '' ? null : $default)->nullable();
                break;
        }
    }

    private function editColumn(
        Blueprint $table,
        string $type,
        string $name,
        mixed $default = ''
    ) {
        switch ($type) {
            case 'string':
                $table->string($name)->default($default == '' ? null : $default)->change();
                break;
            case 'decimal':
                $table->decimal($name)->default($default == '' ? null : $default)->change();
                break;
            case 'text':
                $table->text($name)->default($default == '' ? null : $default)->change();
                break;
            case 'integer':
                $table->integer($name)->default($default == '' ? null : $default)->change();
                break;
            case 'datetime':
                $table->dateTime($name)->default($default == '' ? null : $default)->change();
                break;
        }
    }
}
